// OpenGL.cpp : implement file
//

#include "stdafx.h"
#include "OpenGL.h"


#ifdef _DEBUG
#define new DEBUG_NEW
#endif

// COpenGL

COpenGL::COpenGL()
{
	m_pDC = NULL;
	m_hRC = NULL;

	m_dAspectPrint = 1.0;
	m_dNearPlane   = 1;
	m_dFarPlane    = 10000.0;
	m_dMaxObjSize  = 1000.0; 
	m_fRadius    = float(m_dNearPlane + m_dMaxObjSize / 2.0f); 

	m_ClearColorRed   = 0.00f;
	m_ClearColorGreen = 0.00f;
	m_ClearColorBlue  = 0.00f;

	m_pGraphPlotter = new COpenGLPlotter();
}

COpenGL::~COpenGL()
{
	if (::wglGetCurrentContext())
		::wglMakeCurrent(NULL, NULL);
	
	if (m_hRC != NULL)
	{
		::wglDeleteContext(m_hRC);
		m_hRC = NULL;
	}

	if (m_pDC != NULL)
	{
		delete m_pDC;
		m_pDC = NULL;
	}

	if (m_pGraphPlotter != NULL)
	{
		delete m_pGraphPlotter;
		m_pGraphPlotter = NULL;
	}
}

bool COpenGL::OnInitialize(CView* pView)
{
	m_pDC = new CClientDC(pView);
    ASSERT(m_pDC);

	if (!SetDCPixelFormat(m_pDC->GetSafeHdc(), 
		PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER | PFD_STEREO_DONTCARE))
		return false;

	m_hRC = ::wglCreateContext(m_pDC->GetSafeHdc());
	ASSERT(m_hRC);
	if(!::wglMakeCurrent(m_pDC->GetSafeHdc(), m_hRC))
		return false;

	SetOpenGLState();

	return true;
}

bool COpenGL::SetDCPixelFormat(HDC hDC, DWORD dwFlags)
{
	PIXELFORMATDESCRIPTOR pixelDesc;
	
	pixelDesc.nSize = sizeof(PIXELFORMATDESCRIPTOR);
	pixelDesc.nVersion = 1;
	
	pixelDesc.dwFlags = dwFlags;
	pixelDesc.iPixelType = PFD_TYPE_RGBA;
	pixelDesc.cColorBits = 24;
	pixelDesc.cRedBits = 8;
	pixelDesc.cRedShift = 16;
	pixelDesc.cGreenBits = 8;
	pixelDesc.cGreenShift = 8;
	pixelDesc.cBlueBits = 8;
	pixelDesc.cBlueShift = 0;
	pixelDesc.cAlphaBits = 0;
	pixelDesc.cAlphaShift = 0;
	pixelDesc.cAccumBits = 64;
	pixelDesc.cAccumRedBits = 16;
	pixelDesc.cAccumGreenBits = 16;
	pixelDesc.cAccumBlueBits = 16;
	pixelDesc.cAccumAlphaBits = 0;
	pixelDesc.cDepthBits = 32;
	pixelDesc.cStencilBits = 8;
	pixelDesc.cAuxBuffers = 0;
	pixelDesc.iLayerType = PFD_MAIN_PLANE;
	pixelDesc.bReserved = 0;
	pixelDesc.dwLayerMask = 0;
	pixelDesc.dwVisibleMask = 0;
	pixelDesc.dwDamageMask = 0;
	
	int nPixelIndex = ::ChoosePixelFormat(hDC, &pixelDesc);
	if (nPixelIndex == 0) // Choose default
	{
		nPixelIndex = 1;
		if (::DescribePixelFormat(hDC, nPixelIndex, 
			sizeof(PIXELFORMATDESCRIPTOR), &pixelDesc) == 0)
			return false;
	}

	if (!::SetPixelFormat(hDC, nPixelIndex, &pixelDesc))
		return false;

	return true;
}

void COpenGL::SetOpenGLState()
{
	// Default mode
	::glEnable(GL_NORMALIZE);
	::glEnable(GL_DEPTH_TEST);

	// Lights, material properties
	::glClearColor(m_ClearColorRed, m_ClearColorGreen, m_ClearColorBlue, 1.0f);
	::glClearDepth(1.0);
	::glShadeModel(GL_SMOOTH);

	// Default polygonmode
	::glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}

void COpenGL::OnSize(int cx, int cy)
{
	::glViewport(0, 0, cx, cy);
	m_dAspect = (cy == 0) ? (double)cx : (double)cx/(double)cy;
	SetFrustum();

	m_pGViewer->SetViewRange(cx, cy);

	return __super::OnSize(cx, cy);
}

void COpenGL::SetFrustum()
{
	::glMatrixMode(GL_PROJECTION);
	::glLoadIdentity();
	::gluPerspective(45.0, m_dAspect, m_dNearPlane, m_dFarPlane); 
	::glMatrixMode(GL_MODELVIEW);
}

void COpenGL::OnRender()
{
	::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	CCamera* pCamera = m_pGViewer->GetCamera();
	gluLookAt((*pCamera)[0], (*pCamera)[1], (*pCamera)[2],
		(*pCamera)[3], (*pCamera)[4], (*pCamera)[5],
		(*pCamera)[6], (*pCamera)[7], (*pCamera)[8]);

	GraphicsCollection* pCollection = m_oGraphicsMgr.GetCollection();
	for (GraphicsCollection::iterator it = pCollection->begin();
		it != pCollection->end(); it ++)
	{
		(*it)->Render();
	}
}

void COpenGL::Translate2DTo3D(int iPtX, int iPtY, CVec& pt)
{
	GLint viewport[4];
	GLdouble modelview[16];
	GLdouble projection[16];

	glGetDoublev(GL_MODELVIEW_MATRIX, modelview);
	glGetDoublev(GL_PROJECTION_MATRIX, projection);
	glGetIntegerv(GL_VIEWPORT, viewport);

	GLdouble posX, posY, posZ;
	GLdouble winX, winY;
	winX = (GLdouble)iPtX;
	winY = (GLdouble)viewport[3] - (GLdouble)iPtY;
	glReadBuffer(GL_BACK);
	gluUnProject(winX, winY, 1, modelview, projection, viewport, &posX, &posY, &posZ);

	pt = CVec(posX, posY, posZ);
}

void COpenGL::Translate3DTo2D(const CVec& pt, int& iPtX, int& iPtY)
{
	GLint viewport[4];
	GLdouble modelview[16];
	GLdouble projection[16];

	glGetDoublev(GL_MODELVIEW_MATRIX, modelview);
	glGetDoublev(GL_PROJECTION_MATRIX, projection);
	glGetIntegerv(GL_VIEWPORT, viewport);

	GLdouble posX = pt[0];
	GLdouble posY = pt[1];
	GLdouble posZ = pt[2];
	GLdouble winX, winY, winZ;
	glReadBuffer(GL_BACK);
	gluProject(posX, posY, posZ, modelview, projection, viewport, &winX, &winY, &winZ);

	iPtX = winX;
	iPtY = winY;
}